// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/resources/resource_provider.h"

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <limits>
#include <unordered_map>

#include "base/atomic_sequence_num.h"
#include "base/macros.h"
#include "base/metrics/histogram.h"
#include "base/numerics/safe_math.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event.h"
#include "cc/resources/platform_color.h"
#include "cc/resources/resource_util.h"
#include "cc/resources/returned_resource.h"
#include "cc/resources/shared_bitmap_manager.h"
#include "cc/resources/transferable_resource.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
//#include "skia/ext/texture_handle.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "third_party/skia/include/gpu/GrTextureProvider.h"
//#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"
// #include "ui/gfx/gpu_memory_buffer_tracing.h"
// #include "ui/gfx/icc_profile.h"
#include "ui/gl/trace_util.h"

using gpu::gles2::GLES2Interface;

namespace cc {

class IdAllocator {
public:
    virtual ~IdAllocator() { }

    virtual GLuint NextId() = 0;

protected:
    IdAllocator(GLES2Interface* gl, size_t id_allocation_chunk_size)
        : gl_(gl)
        , id_allocation_chunk_size_(id_allocation_chunk_size)
        , ids_(new GLuint[id_allocation_chunk_size])
        , next_id_index_(id_allocation_chunk_size)
    {
        DCHECK(id_allocation_chunk_size_);
        DCHECK_LE(id_allocation_chunk_size_,
            static_cast<size_t>(std::numeric_limits<int>::max()));
    }

    GLES2Interface* gl_;
    const size_t id_allocation_chunk_size_;
    std::unique_ptr<GLuint[]> ids_;
    size_t next_id_index_;
};

namespace {

    bool IsGpuResourceType(ResourceProvider::ResourceType type)
    {
        return type != ResourceProvider::RESOURCE_TYPE_BITMAP;
    }

    GLenum TextureToStorageFormat(ResourceFormat format)
    {
        GLenum storage_format = GL_RGBA8_OES;
        switch (format) {
        case RGBA_8888:
            break;
        case BGRA_8888:
            storage_format = GL_BGRA8_EXT;
            break;
        case RGBA_4444:
        case ALPHA_8:
        case LUMINANCE_8:
        case RGB_565:
        case ETC1:
        case RED_8:
        case LUMINANCE_F16:
            NOTREACHED();
            break;
        }

        return storage_format;
    }

    bool IsFormatSupportedForStorage(ResourceFormat format, bool use_bgra)
    {
        switch (format) {
        case RGBA_8888:
            return true;
        case BGRA_8888:
            return use_bgra;
        case RGBA_4444:
        case ALPHA_8:
        case LUMINANCE_8:
        case RGB_565:
        case ETC1:
        case RED_8:
        case LUMINANCE_F16:
            return false;
        }
        return false;
    }

    GrPixelConfig ToGrPixelConfig(ResourceFormat format)
    {
        switch (format) {
        case RGBA_8888:
            return kRGBA_8888_GrPixelConfig;
        case BGRA_8888:
            return kBGRA_8888_GrPixelConfig;
        case RGBA_4444:
            return kRGBA_4444_GrPixelConfig;
        default:
            break;
        }
        DCHECK(false) << "Unsupported resource format.";
        return kSkia8888_GrPixelConfig;
    }

    class ScopedSetActiveTexture {
    public:
        ScopedSetActiveTexture(GLES2Interface* gl, GLenum unit)
            : gl_(gl)
            , unit_(unit)
        {
            DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(gl_));

            if (unit_ != GL_TEXTURE0)
                gl_->ActiveTexture(unit_);
        }

        ~ScopedSetActiveTexture()
        {
            // Active unit being GL_TEXTURE0 is effectively the ground state.
            if (unit_ != GL_TEXTURE0)
                gl_->ActiveTexture(GL_TEXTURE0);
        }

    private:
        GLES2Interface* gl_;
        GLenum unit_;
    };

    class TextureIdAllocator : public IdAllocator {
    public:
        TextureIdAllocator(GLES2Interface* gl,
            size_t texture_id_allocation_chunk_size)
            : IdAllocator(gl, texture_id_allocation_chunk_size)
        {
        }
        ~TextureIdAllocator() override
        {
            gl_->DeleteTextures(
                static_cast<int>(id_allocation_chunk_size_ - next_id_index_),
                ids_.get() + next_id_index_);
        }

        // Overridden from IdAllocator:
        GLuint NextId() override
        {
            if (next_id_index_ == id_allocation_chunk_size_) {
                gl_->GenTextures(static_cast<int>(id_allocation_chunk_size_), ids_.get());
                next_id_index_ = 0;
            }

            return ids_[next_id_index_++];
        }

    private:
        DISALLOW_COPY_AND_ASSIGN(TextureIdAllocator);
    };

    class BufferIdAllocator : public IdAllocator {
    public:
        BufferIdAllocator(GLES2Interface* gl, size_t buffer_id_allocation_chunk_size)
            : IdAllocator(gl, buffer_id_allocation_chunk_size)
        {
        }
        ~BufferIdAllocator() override
        {
            gl_->DeleteBuffers(
                static_cast<int>(id_allocation_chunk_size_ - next_id_index_),
                ids_.get() + next_id_index_);
        }

        // Overridden from IdAllocator:
        GLuint NextId() override
        {
            if (next_id_index_ == id_allocation_chunk_size_) {
                gl_->GenBuffers(static_cast<int>(id_allocation_chunk_size_), ids_.get());
                next_id_index_ = 0;
            }

            return ids_[next_id_index_++];
        }

    private:
        DISALLOW_COPY_AND_ASSIGN(BufferIdAllocator);
    };

    // Generates process-unique IDs to use for tracing a ResourceProvider's
    // resources.
    base::StaticAtomicSequenceNumber g_next_resource_provider_tracing_id;

} // namespace

ResourceProvider::Resource::~Resource() { }

ResourceProvider::Resource::Resource(GLuint texture_id,
    const gfx::Size& size,
    Origin origin,
    GLenum target,
    GLenum filter,
    TextureHint hint,
    ResourceType type,
    ResourceFormat format)
    : child_id(0)
    , gl_id(texture_id)
    , gl_pixel_buffer_id(0)
    , gl_upload_query_id(0)
    , gl_read_lock_query_id(0)
    , pixels(nullptr)
    , lock_for_read_count(0)
    , imported_count(0)
    , exported_count(0)
    , dirty_image(false)
    , locked_for_write(false)
    , lost(false)
    , marked_for_deletion(false)
    , allocated(false)
    , read_lock_fences_enabled(false)
    , has_shared_bitmap_id(false)
    , is_overlay_candidate(false)
    ,
#if defined(OS_ANDROID)
    is_backed_by_surface_texture(false)
    , wants_promotion_hint(false)
    ,
#endif
    read_lock_fence(nullptr)
    , size(size)
    , origin(origin)
    , target(target)
    , original_filter(filter)
    , filter(filter)
    , image_id(0)
    , bound_image_id(0)
    , hint(hint)
    , type(type)
    , usage(gfx::BufferUsage::GPU_READ_CPU_READ_WRITE)
    , format(format)
    , shared_bitmap(nullptr)
{
}

ResourceProvider::Resource::Resource(uint8_t* pixels,
    SharedBitmap* bitmap,
    const gfx::Size& size,
    Origin origin,
    GLenum filter)
    : child_id(0)
    , gl_id(0)
    , gl_pixel_buffer_id(0)
    , gl_upload_query_id(0)
    , gl_read_lock_query_id(0)
    , pixels(pixels)
    , lock_for_read_count(0)
    , imported_count(0)
    , exported_count(0)
    , dirty_image(false)
    , locked_for_write(false)
    , lost(false)
    , marked_for_deletion(false)
    , allocated(false)
    , read_lock_fences_enabled(false)
    , has_shared_bitmap_id(!!bitmap)
    , is_overlay_candidate(false)
    ,
#if defined(OS_ANDROID)
    is_backed_by_surface_texture(false)
    , wants_promotion_hint(false)
    ,
#endif
    read_lock_fence(nullptr)
    , size(size)
    , origin(origin)
    , target(0)
    , original_filter(filter)
    , filter(filter)
    , image_id(0)
    , bound_image_id(0)
    , hint(TEXTURE_HINT_IMMUTABLE)
    , type(RESOURCE_TYPE_BITMAP)
    , format(RGBA_8888)
    , shared_bitmap(bitmap)
{
    DCHECK(origin == DELEGATED || pixels);
    if (bitmap)
        shared_bitmap_id = bitmap->id();
}

ResourceProvider::Resource::Resource(const SharedBitmapId& bitmap_id,
    const gfx::Size& size,
    Origin origin,
    GLenum filter)
    : child_id(0)
    , gl_id(0)
    , gl_pixel_buffer_id(0)
    , gl_upload_query_id(0)
    , gl_read_lock_query_id(0)
    , pixels(nullptr)
    , lock_for_read_count(0)
    , imported_count(0)
    , exported_count(0)
    , dirty_image(false)
    , locked_for_write(false)
    , lost(false)
    , marked_for_deletion(false)
    , allocated(false)
    , read_lock_fences_enabled(false)
    , has_shared_bitmap_id(true)
    , is_overlay_candidate(false)
    ,
#if defined(OS_ANDROID)
    is_backed_by_surface_texture(false)
    , wants_promotion_hint(false)
    ,
#endif
    read_lock_fence(nullptr)
    , size(size)
    , origin(origin)
    , target(0)
    , original_filter(filter)
    , filter(filter)
    , image_id(0)
    , bound_image_id(0)
    , hint(TEXTURE_HINT_IMMUTABLE)
    , type(RESOURCE_TYPE_BITMAP)
    , format(RGBA_8888)
    , shared_bitmap_id(bitmap_id)
    , shared_bitmap(nullptr)
{
}

ResourceProvider::Resource::Resource(Resource&& other) = default;

void ResourceProvider::Resource::set_mailbox(const TextureMailbox& mailbox)
{
    mailbox_ = mailbox;
    if (IsGpuResourceType(type)) {
        synchronization_state_ = (mailbox.sync_token().HasData() ? NEEDS_WAIT : LOCALLY_USED);
        needs_sync_token_ = !mailbox.sync_token().HasData();
    } else {
        synchronization_state_ = SYNCHRONIZED;
    }
}

void ResourceProvider::Resource::SetLocallyUsed()
{
    synchronization_state_ = LOCALLY_USED;
    mailbox_.set_sync_token(gpu::SyncToken());
    needs_sync_token_ = IsGpuResourceType(type);
}

void ResourceProvider::Resource::SetSynchronized()
{
    synchronization_state_ = SYNCHRONIZED;
}

void ResourceProvider::Resource::UpdateSyncToken(
    const gpu::SyncToken& sync_token)
{
    // In the case of context lost, this sync token may be empty since sync tokens
    // may not be generated unless a successful flush occurred. However, we will
    // assume the task runner is calling this function properly and update the
    // state accordingly.
    mailbox_.set_sync_token(sync_token);
    synchronization_state_ = NEEDS_WAIT;
    needs_sync_token_ = false;
}

int8_t* ResourceProvider::Resource::GetSyncTokenData()
{
    return mailbox_.GetSyncTokenData();
}

void ResourceProvider::Resource::WaitSyncToken(gpu::gles2::GLES2Interface* gl)
{
    // Make sure we are only called when state actually needs to wait.
    DCHECK_EQ(NEEDS_WAIT, synchronization_state_);

    // Make sure sync token is not stale.
    DCHECK(!needs_sync_token_);

    // In the case of context lost, this sync token may be empty (see comment in
    // the UpdateSyncToken() function). The WaitSyncTokenCHROMIUM() function
    // handles empty sync tokens properly so just wait anyways and update the
    // state the synchronized.
    gl->WaitSyncTokenCHROMIUM(mailbox_.sync_token().GetConstData());
    SetSynchronized();
}

ResourceProvider::Child::Child()
    : marked_for_deletion(false)
    , needs_sync_tokens(true)
{
}

ResourceProvider::Child::Child(const Child& other) = default;

ResourceProvider::Child::~Child() { }

ResourceProvider::ResourceProvider(
    ContextProvider* compositor_context_provider,
    SharedBitmapManager* shared_bitmap_manager,
    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
    BlockingTaskRunner* blocking_main_thread_task_runner,
    int highp_threshold_min,
    size_t id_allocation_chunk_size,
    bool delegated_sync_points_required,
    bool use_gpu_memory_buffer_resources,
    bool enable_color_correct_rendering,
    const BufferToTextureTargetMap& buffer_to_texture_target_map)
    : compositor_context_provider_(compositor_context_provider)
    , shared_bitmap_manager_(shared_bitmap_manager)
    , gpu_memory_buffer_manager_(gpu_memory_buffer_manager)
    , blocking_main_thread_task_runner_(blocking_main_thread_task_runner)
    , lost_context_provider_(false)
    , highp_threshold_min_(highp_threshold_min)
    , next_id_(1)
    , next_child_(1)
    , delegated_sync_points_required_(delegated_sync_points_required)
    , default_resource_type_(use_gpu_memory_buffer_resources
              ? RESOURCE_TYPE_GPU_MEMORY_BUFFER
              : RESOURCE_TYPE_GL_TEXTURE)
    , use_texture_storage_ext_(false)
    , use_texture_format_bgra_(false)
    , use_texture_usage_hint_(false)
    , use_compressed_texture_etc1_(false)
    , yuv_resource_format_(LUMINANCE_8)
    , max_texture_size_(0)
    , best_texture_format_(RGBA_8888)
    , best_render_buffer_format_(RGBA_8888)
    , enable_color_correct_rendering_(enable_color_correct_rendering)
    , id_allocation_chunk_size_(id_allocation_chunk_size)
    , use_sync_query_(false)
    , buffer_to_texture_target_map_(buffer_to_texture_target_map)
    , tracing_id_(g_next_resource_provider_tracing_id.GetNext())
{
    DCHECK(id_allocation_chunk_size_);
    DCHECK(thread_checker_.CalledOnValidThread());

    // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview).
    // Don't register a dump provider in these cases.
    // TODO(ericrk): Get this working in Android Webview. crbug.com/517156
    if (base::ThreadTaskRunnerHandle::IsSet()) {
        base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
            this, "cc::ResourceProvider", base::ThreadTaskRunnerHandle::Get());
    }

    if (!compositor_context_provider_) {
        default_resource_type_ = RESOURCE_TYPE_BITMAP;
        // Pick an arbitrary limit here similar to what hardware might.
        max_texture_size_ = 16 * 1024;
        best_texture_format_ = RGBA_8888;
        return;
    }

    DCHECK(!texture_id_allocator_);
    DCHECK(!buffer_id_allocator_);

    const auto& caps = compositor_context_provider_->ContextCapabilities();

    DCHECK(IsGpuResourceType(default_resource_type_));
    use_texture_storage_ext_ = caps.texture_storage;
    use_texture_format_bgra_ = caps.texture_format_bgra8888;
    use_texture_usage_hint_ = caps.texture_usage;
    use_compressed_texture_etc1_ = caps.texture_format_etc1;

    //   if (caps.disable_one_component_textures) {
    //     yuv_resource_format_ = yuv_highbit_resource_format_ = RGBA_8888;
    //   } else {
    //     yuv_resource_format_ = caps.texture_rg ? RED_8 : LUMINANCE_8;
    //     yuv_highbit_resource_format_ =
    //         caps.texture_half_float_linear ? LUMINANCE_F16 : yuv_resource_format_;
    //   }
    DebugBreak();
    use_sync_query_ = caps.sync_query;

    GLES2Interface* gl = ContextGL();

    max_texture_size_ = 0; // Context expects cleared value.
    gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
    best_texture_format_ = PlatformColor::BestSupportedTextureFormat(use_texture_format_bgra_);

    best_render_buffer_format_ = PlatformColor::BestSupportedTextureFormat(
        caps.render_buffer_format_bgra8888);

    texture_id_allocator_.reset(
        new TextureIdAllocator(gl, id_allocation_chunk_size_));
    buffer_id_allocator_.reset(
        new BufferIdAllocator(gl, id_allocation_chunk_size_));
}

ResourceProvider::~ResourceProvider()
{
    base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
        this);

    while (!children_.empty())
        DestroyChildInternal(children_.begin(), FOR_SHUTDOWN);
    while (!resources_.empty())
        DeleteResourceInternal(resources_.begin(), FOR_SHUTDOWN);

    GLES2Interface* gl = ContextGL();
    if (!IsGpuResourceType(default_resource_type_)) {
        // We are not in GL mode, but double check before returning.
        DCHECK(!gl);
        return;
    }

    DCHECK(gl);
#if DCHECK_IS_ON()
    // Check that all GL resources has been deleted.
    for (ResourceMap::const_iterator itr = resources_.begin();
         itr != resources_.end(); ++itr) {
        DCHECK(!IsGpuResourceType(itr->second.type));
    }
#endif // DCHECK_IS_ON()

    texture_id_allocator_ = nullptr;
    buffer_id_allocator_ = nullptr;
    gl->Finish();
}

bool ResourceProvider::IsResourceFormatSupported(ResourceFormat format) const
{
    gpu::Capabilities caps;
    if (compositor_context_provider_)
        caps = compositor_context_provider_->ContextCapabilities();

    switch (format) {
    case ALPHA_8:
    case RGBA_4444:
    case RGBA_8888:
    case RGB_565:
    case LUMINANCE_8:
        return true;
    case BGRA_8888:
        return caps.texture_format_bgra8888;
    case ETC1:
        return caps.texture_format_etc1;
    case RED_8:
        return caps.texture_rg;
        //     case LUMINANCE_F16:
        //       return caps.texture_half_float_linear;
    }

    NOTREACHED();
    return false;
}

bool ResourceProvider::InUseByConsumer(ResourceId id)
{
    Resource* resource = GetResource(id);
    return resource->lock_for_read_count > 0 || resource->exported_count > 0 || resource->lost;
}

bool ResourceProvider::IsLost(ResourceId id)
{
    Resource* resource = GetResource(id);
    return resource->lost;
}

void ResourceProvider::LoseResourceForTesting(ResourceId id)
{
    Resource* resource = GetResource(id);
    DCHECK(resource);
    resource->lost = true;
}

ResourceFormat ResourceProvider::YuvResourceFormat(int bits) const
{
    if (bits > 8) {
        return yuv_highbit_resource_format_;
    } else {
        return yuv_resource_format_;
    }
}

ResourceId ResourceProvider::CreateResource(
    const gfx::Size& size,
    TextureHint hint,
    ResourceFormat format,
    const gfx::ColorSpace& color_space)
{
    DCHECK(!size.IsEmpty());
    switch (default_resource_type_) {
    case RESOURCE_TYPE_GPU_MEMORY_BUFFER:
        // GPU memory buffers don't support LUMINANCE_F16.
        if (format != LUMINANCE_F16) {
            return CreateGLTexture(
                size, hint, RESOURCE_TYPE_GPU_MEMORY_BUFFER, format,
                gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, color_space);
        }
    // Fall through and use a regular texture.
    case RESOURCE_TYPE_GL_TEXTURE:
        return CreateGLTexture(size, hint, RESOURCE_TYPE_GL_TEXTURE, format,
            gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
            color_space);

    case RESOURCE_TYPE_BITMAP:
        DCHECK_EQ(RGBA_8888, format);
        return CreateBitmap(size, color_space);
    }

    LOG(FATAL) << "Invalid default resource type.";
    return 0;
}

ResourceId ResourceProvider::CreateGpuMemoryBufferResource(
    const gfx::Size& size,
    TextureHint hint,
    ResourceFormat format,
    gfx::BufferUsage usage,
    const gfx::ColorSpace& color_space)
{
    DCHECK(!size.IsEmpty());
    switch (default_resource_type_) {
    case RESOURCE_TYPE_GPU_MEMORY_BUFFER:
    case RESOURCE_TYPE_GL_TEXTURE: {
        return CreateGLTexture(size, hint, RESOURCE_TYPE_GPU_MEMORY_BUFFER,
            format, usage, color_space);
    }
    case RESOURCE_TYPE_BITMAP:
        DCHECK_EQ(RGBA_8888, format);
        return CreateBitmap(size, color_space);
    }

    LOG(FATAL) << "Invalid default resource type.";
    return 0;
}

ResourceId ResourceProvider::CreateGLTexture(
    const gfx::Size& size,
    TextureHint hint,
    ResourceType type,
    ResourceFormat format,
    gfx::BufferUsage usage,
    const gfx::ColorSpace& color_space)
{
    DCHECK_LE(size.width(), max_texture_size_);
    DCHECK_LE(size.height(), max_texture_size_);
    DCHECK(thread_checker_.CalledOnValidThread());

    // TODO(crbug.com/590317): We should not assume that all resources created by
    // ResourceProvider are GPU_READ_CPU_READ_WRITE. We should determine this
    // based on the current RasterBufferProvider's needs.
    GLenum target = type == RESOURCE_TYPE_GPU_MEMORY_BUFFER
        ? GetImageTextureTarget(usage, format)
        : GL_TEXTURE_2D;

    ResourceId id = next_id_++;
    Resource* resource = InsertResource(id, Resource(0, size, Resource::INTERNAL, target, GL_LINEAR, hint, type, format));
    resource->usage = usage;
    resource->allocated = false;
    resource->color_space = color_space;
    return id;
}

ResourceId ResourceProvider::CreateBitmap(const gfx::Size& size,
    const gfx::ColorSpace& color_space)
{
    DCHECK(thread_checker_.CalledOnValidThread());

    std::unique_ptr<SharedBitmap> bitmap = shared_bitmap_manager_->AllocateSharedBitmap(size);
    uint8_t* pixels = bitmap->pixels();
    DCHECK(pixels);

    ResourceId id = next_id_++;
    Resource* resource = InsertResource(
        id,
        Resource(pixels, bitmap.release(), size, Resource::INTERNAL, GL_LINEAR));
    resource->allocated = true;
    resource->color_space = color_space;
    return id;
}

ResourceId ResourceProvider::CreateResourceFromTextureMailbox(
    const TextureMailbox& mailbox,
    std::unique_ptr<SingleReleaseCallbackImpl> release_callback_impl,
    bool read_lock_fences_enabled)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    // Just store the information. Mailbox will be consumed in LockForRead().
    ResourceId id = next_id_++;
    DCHECK(mailbox.IsValid());
    Resource* resource = nullptr;
    if (mailbox.IsTexture()) {
        resource = InsertResource(
            id,
            Resource(0, mailbox.size_in_pixels(), Resource::EXTERNAL,
                mailbox.target(),
                mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR,
                TEXTURE_HINT_IMMUTABLE, RESOURCE_TYPE_GL_TEXTURE, RGBA_8888));
    } else {
        DCHECK(mailbox.IsSharedMemory());
        SharedBitmap* shared_bitmap = mailbox.shared_bitmap();
        uint8_t* pixels = shared_bitmap->pixels();
        DCHECK(pixels);
        resource = InsertResource(
            id, Resource(pixels, shared_bitmap, mailbox.size_in_pixels(), Resource::EXTERNAL, GL_LINEAR));
    }
    resource->allocated = true;
    resource->set_mailbox(mailbox);
    resource->color_space = mailbox.color_space();
    resource->release_callback_impl = base::Bind(&SingleReleaseCallbackImpl::Run,
        base::Owned(release_callback_impl.release()));
    resource->read_lock_fences_enabled = read_lock_fences_enabled;
    resource->is_overlay_candidate = mailbox.is_overlay_candidate();
#if defined(OS_ANDROID)
    resource->is_backed_by_surface_texture = mailbox.is_backed_by_surface_texture();
    resource->wants_promotion_hint = mailbox.wants_promotion_hint();
    if (resource->wants_promotion_hint)
        wants_promotion_hints_set_.insert(id);
#endif
    resource->color_space = mailbox.color_space();

    return id;
}

ResourceId ResourceProvider::CreateResourceFromTextureMailbox(
    const TextureMailbox& mailbox,
    std::unique_ptr<SingleReleaseCallbackImpl> release_callback_impl)
{
    return CreateResourceFromTextureMailbox(
        mailbox, std::move(release_callback_impl), false);
}

void ResourceProvider::DeleteResource(ResourceId id)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    ResourceMap::iterator it = resources_.find(id);
    CHECK(it != resources_.end());
    Resource* resource = &it->second;
    DCHECK(!resource->marked_for_deletion);
    DCHECK_EQ(resource->imported_count, 0);
    DCHECK(!resource->locked_for_write);

    if (resource->exported_count > 0 || resource->lock_for_read_count > 0 || !ReadLockFenceHasPassed(resource)) {
        resource->marked_for_deletion = true;
        return;
    } else {
        DeleteResourceInternal(it, NORMAL);
    }
}

void ResourceProvider::DeleteResourceInternal(ResourceMap::iterator it,
    DeleteStyle style)
{
    TRACE_EVENT0("cc", "ResourceProvider::DeleteResourceInternal");
    Resource* resource = &it->second;
    DCHECK(resource->exported_count == 0 || style != NORMAL);

#if defined(OS_ANDROID)
    // If this resource was interested in promotion hints, then remove it from
    // the set of resources that we'll notify.
    if (resource->wants_promotion_hint)
        wants_promotion_hints_set_.erase(it->first);
#endif

    // Exported resources are lost on shutdown.
    bool exported_resource_lost = style == FOR_SHUTDOWN && resource->exported_count > 0;
    // GPU resources are lost when context is lost.
    bool gpu_resource_lost = IsGpuResourceType(resource->type) && lost_context_provider_;
    bool lost_resource = resource->lost || exported_resource_lost || gpu_resource_lost;

    // Wait on sync token before deleting resources we own.
    if (!lost_resource && resource->origin == Resource::INTERNAL && resource->synchronization_state() == Resource::NEEDS_WAIT) {
        DCHECK(resource->allocated);
        DCHECK(IsGpuResourceType(resource->type));
        GLES2Interface* gl = ContextGL();
        DCHECK(gl);
        resource->WaitSyncToken(gl);
    }

    if (resource->image_id) {
        DCHECK(resource->origin == Resource::INTERNAL);
        GLES2Interface* gl = ContextGL();
        DCHECK(gl);
        gl->DestroyImageCHROMIUM(resource->image_id);
    }
    if (resource->gl_upload_query_id) {
        DCHECK(resource->origin == Resource::INTERNAL);
        GLES2Interface* gl = ContextGL();
        DCHECK(gl);
        gl->DeleteQueriesEXT(1, &resource->gl_upload_query_id);
    }
    if (resource->gl_read_lock_query_id) {
        DCHECK(resource->origin == Resource::INTERNAL);
        GLES2Interface* gl = ContextGL();
        DCHECK(gl);
        gl->DeleteQueriesEXT(1, &resource->gl_read_lock_query_id);
    }
    if (resource->gl_pixel_buffer_id) {
        DCHECK(resource->origin == Resource::INTERNAL);
        GLES2Interface* gl = ContextGL();
        DCHECK(gl);
        gl->DeleteBuffers(1, &resource->gl_pixel_buffer_id);
    }
    if (resource->origin == Resource::EXTERNAL) {
        DCHECK(resource->mailbox().IsValid());
        gpu::SyncToken sync_token = resource->mailbox().sync_token();
        if (IsGpuResourceType(resource->type)) {
            DCHECK(resource->mailbox().IsTexture());
            GLES2Interface* gl = ContextGL();
            DCHECK(gl);
            if (resource->gl_id) {
                DCHECK_NE(Resource::NEEDS_WAIT, resource->synchronization_state());
                gl->DeleteTextures(1, &resource->gl_id);
                resource->gl_id = 0;
                if (!lost_resource) {
                    const GLuint64 fence_sync = gl->InsertFenceSyncCHROMIUM();
                    gl->ShallowFlushCHROMIUM();
                    gl->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
                }
            }
        } else {
            DCHECK(resource->mailbox().IsSharedMemory());
            resource->shared_bitmap = nullptr;
            resource->pixels = nullptr;
        }
        resource->release_callback_impl.Run(sync_token, lost_resource,
            blocking_main_thread_task_runner_);
    }
    if (resource->gl_id) {
        GLES2Interface* gl = ContextGL();
        DCHECK(gl);
        gl->DeleteTextures(1, &resource->gl_id);
        resource->gl_id = 0;
    }
    if (resource->shared_bitmap) {
        DCHECK(resource->origin != Resource::EXTERNAL);
        DCHECK_EQ(RESOURCE_TYPE_BITMAP, resource->type);
        delete resource->shared_bitmap;
        resource->pixels = nullptr;
    }
    if (resource->pixels) {
        DCHECK(resource->origin == Resource::INTERNAL);
        delete[] resource->pixels;
        resource->pixels = nullptr;
    }
    if (resource->gpu_memory_buffer) {
        DCHECK(resource->origin == Resource::INTERNAL || resource->origin == Resource::DELEGATED);
        resource->gpu_memory_buffer.reset();
    }
    resources_.erase(it);
}

void ResourceProvider::FlushPendingDeletions() const
{
    if (auto* gl = ContextGL())
        gl->ShallowFlushCHROMIUM();
}

ResourceProvider::ResourceType ResourceProvider::GetResourceType(
    ResourceId id)
{
    return GetResource(id)->type;
}

GLenum ResourceProvider::GetResourceTextureTarget(ResourceId id)
{
    return GetResource(id)->target;
}

bool ResourceProvider::IsImmutable(ResourceId id)
{
    if (IsGpuResourceType(default_resource_type_)) {
        return GetTextureHint(id) == TEXTURE_HINT_IMMUTABLE;
    } else {
        // Software resources are immutable; they cannot change format or be
        // resized.
        return true;
    }
}

ResourceProvider::TextureHint ResourceProvider::GetTextureHint(ResourceId id)
{
    return GetResource(id)->hint;
}

sk_sp<SkColorSpace> ResourceProvider::GetResourceSkColorSpace(
    const Resource* resource) const
{
    if (!enable_color_correct_rendering_)
        return nullptr;
    //return resource->color_space.ToSkColorSpace();
    return nullptr;
}

void ResourceProvider::CopyToResource(ResourceId id,
    const uint8_t* image,
    const gfx::Size& image_size)
{
    Resource* resource = GetResource(id);
    DCHECK(!resource->locked_for_write);
    DCHECK(!resource->lock_for_read_count);
    DCHECK(resource->origin == Resource::INTERNAL);
    DCHECK_EQ(resource->exported_count, 0);
    DCHECK(ReadLockFenceHasPassed(resource));

    DCHECK_EQ(image_size.width(), resource->size.width());
    DCHECK_EQ(image_size.height(), resource->size.height());

    if (resource->allocated)
        WaitSyncTokenIfNeeded(id);

    if (resource->type == RESOURCE_TYPE_BITMAP) {
        DCHECK_EQ(RESOURCE_TYPE_BITMAP, resource->type);
        DCHECK(resource->allocated);
        DCHECK_EQ(RGBA_8888, resource->format);
        SkImageInfo source_info = SkImageInfo::MakeN32Premul(image_size.width(), image_size.height(),
            GetResourceSkColorSpace(resource));
        size_t image_stride = image_size.width() * 4;

        ScopedWriteLockSoftware lock(this, id);
        SkCanvas dest(lock.sk_bitmap());
        dest.writePixels(source_info, image, image_stride, 0, 0);
    } else {
        ScopedWriteLockGL lock(this, id, false);
        unsigned resource_texture_id = lock.texture_id();
        DCHECK(resource_texture_id);
        GLES2Interface* gl = ContextGL();
        DCHECK(gl);
        gl->BindTexture(resource->target, resource_texture_id);
        if (resource->format == ETC1) {
            DCHECK_EQ(resource->target, static_cast<GLenum>(GL_TEXTURE_2D));
            int image_bytes = ResourceUtil::CheckedSizeInBytes<int>(image_size, ETC1);
            gl->CompressedTexImage2D(resource->target, 0, GLInternalFormat(ETC1),
                image_size.width(), image_size.height(), 0,
                image_bytes, image);
        } else {
            gl->TexSubImage2D(resource->target, 0, 0, 0, image_size.width(),
                image_size.height(), GLDataFormat(resource->format),
                GLDataType(resource->format), image);
        }
        const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
        gl->OrderingBarrierCHROMIUM();
        gpu::SyncToken sync_token;
        gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
        lock.set_sync_token(sync_token);
        lock.set_synchronized(true);
    }
}

void ResourceProvider::GenerateSyncTokenForResource(ResourceId resource_id)
{
    Resource* resource = GetResource(resource_id);
    if (!resource->needs_sync_token())
        return;

    gpu::SyncToken sync_token;
    GLES2Interface* gl = ContextGL();
    DCHECK(gl);

    const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
    gl->OrderingBarrierCHROMIUM();
    gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());

    resource->UpdateSyncToken(sync_token);
    resource->SetSynchronized();
}

void ResourceProvider::GenerateSyncTokenForResources(
    const ResourceIdArray& resource_ids)
{
    gpu::SyncToken sync_token;
    bool created_sync_token = false;
    for (ResourceId id : resource_ids) {
        Resource* resource = GetResource(id);
        if (resource->needs_sync_token()) {
            if (!created_sync_token) {
                GLES2Interface* gl = ContextGL();
                DCHECK(gl);

                const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
                gl->OrderingBarrierCHROMIUM();
                gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
                created_sync_token = true;
            }

            resource->UpdateSyncToken(sync_token);
            resource->SetSynchronized();
        }
    }
}

ResourceProvider::Resource* ResourceProvider::InsertResource(
    ResourceId id,
    Resource resource)
{
    std::pair<ResourceMap::iterator, bool> result = resources_.insert(ResourceMap::value_type(id, std::move(resource)));
    DCHECK(result.second);
    return &result.first->second;
}

ResourceProvider::Resource* ResourceProvider::GetResource(ResourceId id)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    DCHECK(id);
    ResourceMap::iterator it = resources_.find(id);
    DCHECK(it != resources_.end());
    return &it->second;
}

const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id)
{
    Resource* resource = GetResource(id);
    DCHECK(!resource->locked_for_write) << "locked for write: "
                                        << resource->locked_for_write;
    DCHECK_EQ(resource->exported_count, 0);
    // Uninitialized! Call SetPixels or LockForWrite first.
    DCHECK(resource->allocated);

    // Mailbox sync_tokens must be processed by a call to
    // WaitSyncTokenIfNeeded() prior to calling LockForRead().
    DCHECK_NE(Resource::NEEDS_WAIT, resource->synchronization_state());

    LazyCreate(resource);

    if (IsGpuResourceType(resource->type) && !resource->gl_id) {
        DCHECK(resource->origin != Resource::INTERNAL);
        DCHECK(resource->mailbox().IsTexture());

        GLES2Interface* gl = ContextGL();
        DCHECK(gl);
        resource->gl_id = gl->CreateAndConsumeTextureCHROMIUM(
            resource->mailbox().target(), resource->mailbox().name());
        resource->SetLocallyUsed();
    }

    if (!resource->pixels && resource->has_shared_bitmap_id && shared_bitmap_manager_) {
        std::unique_ptr<SharedBitmap> bitmap = shared_bitmap_manager_->GetSharedBitmapFromId(
            resource->size, resource->shared_bitmap_id);
        if (bitmap) {
            resource->shared_bitmap = bitmap.release();
            resource->pixels = resource->shared_bitmap->pixels();
        }
    }

    resource->lock_for_read_count++;
    if (resource->read_lock_fences_enabled) {
        if (current_read_lock_fence_.get())
            current_read_lock_fence_->Set();
        resource->read_lock_fence = current_read_lock_fence_;
    }

    return resource;
}

void ResourceProvider::UnlockForRead(ResourceId id)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    ResourceMap::iterator it = resources_.find(id);
    CHECK(it != resources_.end());

    Resource* resource = &it->second;
    DCHECK_GT(resource->lock_for_read_count, 0);
    DCHECK_EQ(resource->exported_count, 0);
    resource->lock_for_read_count--;
    if (resource->marked_for_deletion && !resource->lock_for_read_count) {
        if (!resource->child_id) {
            // The resource belongs to this ResourceProvider, so it can be destroyed.
            DeleteResourceInternal(it, NORMAL);
        } else {
            ChildMap::iterator child_it = children_.find(resource->child_id);
            ResourceIdArray unused;
            unused.push_back(id);
            DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
        }
    }
}

ResourceProvider::Resource* ResourceProvider::LockForWrite(ResourceId id)
{
    Resource* resource = GetResource(id);
    DCHECK(CanLockForWrite(id));
    if (resource->allocated)
        WaitSyncTokenIfNeeded(id);
    resource->locked_for_write = true;
    resource->SetLocallyUsed();
    return resource;
}

bool ResourceProvider::CanLockForWrite(ResourceId id)
{
    Resource* resource = GetResource(id);
    return !resource->locked_for_write && !resource->lock_for_read_count && !resource->exported_count && resource->origin == Resource::INTERNAL && !resource->lost && ReadLockFenceHasPassed(resource);
}

bool ResourceProvider::IsOverlayCandidate(ResourceId id)
{
    Resource* resource = GetResource(id);
    return resource->is_overlay_candidate;
}

#if defined(OS_ANDROID)
bool ResourceProvider::IsBackedBySurfaceTexture(ResourceId id)
{
    Resource* resource = GetResource(id);
    return resource->is_backed_by_surface_texture;
}

bool ResourceProvider::WantsPromotionHint(ResourceId id)
{
    return wants_promotion_hints_set_.count(id) > 0;
}

size_t ResourceProvider::CountPromotionHintRequestsForTesting()
{
    return wants_promotion_hints_set_.size();
}
#endif

void ResourceProvider::UnlockForWrite(Resource* resource)
{
    DCHECK(resource->locked_for_write);
    DCHECK_EQ(resource->exported_count, 0);
    DCHECK(resource->origin == Resource::INTERNAL);
    resource->locked_for_write = false;
}

void ResourceProvider::EnableReadLockFencesForTesting(ResourceId id)
{
    Resource* resource = GetResource(id);
    DCHECK(resource);
    resource->read_lock_fences_enabled = true;
}

ResourceProvider::ScopedReadLockGL::ScopedReadLockGL(
    ResourceProvider* resource_provider,
    ResourceId resource_id)
    : resource_provider_(resource_provider)
    , resource_id_(resource_id)
{
    const Resource* resource = resource_provider->LockForRead(resource_id);
    texture_id_ = resource->gl_id;
    target_ = resource->target;
    size_ = resource->size;
    color_space_ = resource->color_space;
}

ResourceProvider::ScopedReadLockGL::~ScopedReadLockGL()
{
    resource_provider_->UnlockForRead(resource_id_);
}

ResourceProvider::ScopedSamplerGL::ScopedSamplerGL(
    ResourceProvider* resource_provider,
    ResourceId resource_id,
    GLenum filter)
    : resource_lock_(resource_provider, resource_id)
    , unit_(GL_TEXTURE0)
    , target_(resource_provider->BindForSampling(resource_id, unit_, filter))
{
}

ResourceProvider::ScopedSamplerGL::ScopedSamplerGL(
    ResourceProvider* resource_provider,
    ResourceId resource_id,
    GLenum unit,
    GLenum filter)
    : resource_lock_(resource_provider, resource_id)
    , unit_(unit)
    , target_(resource_provider->BindForSampling(resource_id, unit_, filter))
{
}

ResourceProvider::ScopedSamplerGL::~ScopedSamplerGL() { }

ResourceProvider::ScopedWriteLockGL::ScopedWriteLockGL(
    ResourceProvider* resource_provider,
    ResourceId resource_id,
    bool create_mailbox)
    : resource_provider_(resource_provider)
    , resource_id_(resource_id)
    , has_sync_token_(false)
    , synchronized_(false)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    Resource* resource = resource_provider->LockForWrite(resource_id);
    resource_provider_->LazyAllocate(resource);
    if (resource->image_id && resource->dirty_image)
        resource_provider_->BindImageForSampling(resource);
    if (create_mailbox) {
        resource_provider_->CreateMailboxAndBindResource(
            resource_provider_->ContextGL(), resource);
    }
    texture_id_ = resource->gl_id;
    target_ = resource->target;
    format_ = resource->format;
    size_ = resource->size;
    mailbox_ = resource->mailbox();
    sk_color_space_ = resource_provider->GetResourceSkColorSpace(resource);
}

ResourceProvider::ScopedWriteLockGL::~ScopedWriteLockGL()
{
    DCHECK(thread_checker_.CalledOnValidThread());
    Resource* resource = resource_provider_->GetResource(resource_id_);
    DCHECK(resource->locked_for_write);
    // It's not sufficient to check sync_token_.HasData() here because the sync
    // might be null because of context loss. Even in that case we want to set the
    // sync token because it's checked in PrepareSendToParent while drawing.
    if (has_sync_token_)
        resource->UpdateSyncToken(sync_token_);
    if (synchronized_)
        resource->SetSynchronized();
    resource_provider_->UnlockForWrite(resource);
}

ResourceProvider::ScopedTextureProvider::ScopedTextureProvider(
    gpu::gles2::GLES2Interface* gl,
    ScopedWriteLockGL* resource_lock,
    bool use_mailbox)
    : gl_(gl)
    , use_mailbox_(use_mailbox)
{
    if (use_mailbox_) {
        texture_id_ = gl_->CreateAndConsumeTextureCHROMIUM(
            resource_lock->target(), resource_lock->mailbox().name());
    } else {
        texture_id_ = resource_lock->texture_id();
    }
    DCHECK(texture_id_);
}

ResourceProvider::ScopedTextureProvider::~ScopedTextureProvider()
{
    if (use_mailbox_)
        gl_->DeleteTextures(1, &texture_id_);
}

ResourceProvider::ScopedSkSurfaceProvider::ScopedSkSurfaceProvider(
    ContextProvider* context_provider,
    ScopedWriteLockGL* resource_lock,
    bool use_mailbox,
    bool use_distance_field_text,
    bool can_use_lcd_text,
    bool ignore_color_space,
    int msaa_sample_count)
    : texture_provider_(context_provider->ContextGL(),
        resource_lock,
        use_mailbox)
{
    //   GrGLTextureInfo texture_info;
    //   texture_info.fID = texture_provider_.texture_id();
    //   texture_info.fTarget = resource_lock->target();
    //   GrBackendTextureDesc desc;
    //   desc.fFlags = kRenderTarget_GrBackendTextureFlag;
    //   desc.fWidth = resource_lock->size().width();
    //   desc.fHeight = resource_lock->size().height();
    //   desc.fConfig = ToGrPixelConfig(resource_lock->format());
    //   desc.fOrigin = kTopLeft_GrSurfaceOrigin;
    //   desc.fTextureHandle = skia::GrGLTextureInfoToGrBackendObject(texture_info);
    //   desc.fSampleCnt = msaa_sample_count;
    //
    //   uint32_t flags =
    //       use_distance_field_text ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;
    //   // Use unknown pixel geometry to disable LCD text.
    //   SkSurfaceProps surface_props(flags, kUnknown_SkPixelGeometry);
    //   if (can_use_lcd_text) {
    //     // LegacyFontHost will get LCD text and skia figures out what type to use.
    //     surface_props =
    //         SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
    //   }
    //   sk_surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget(
    //       context_provider->GrContext(), desc,
    //       ignore_color_space ? nullptr : resource_lock->sk_color_space(),
    //       &surface_props);
    DebugBreak();
}

ResourceProvider::ScopedSkSurfaceProvider::~ScopedSkSurfaceProvider()
{
    if (sk_surface_.get()) {
        sk_surface_->prepareForExternalIO();
        sk_surface_.reset();
    }
}

void ResourceProvider::PopulateSkBitmapWithResource(SkBitmap* sk_bitmap,
    const Resource* resource)
{
    DCHECK_EQ(RGBA_8888, resource->format);
    SkImageInfo info = SkImageInfo::MakeN32Premul(
        resource->size.width(), resource->size.height(),
        GetResourceSkColorSpace(resource));
    sk_bitmap->installPixels(info, resource->pixels, info.minRowBytes());
}

ResourceProvider::ScopedReadLockSoftware::ScopedReadLockSoftware(
    ResourceProvider* resource_provider,
    ResourceId resource_id)
    : resource_provider_(resource_provider)
    , resource_id_(resource_id)
{
    const Resource* resource = resource_provider->LockForRead(resource_id);
    resource_provider->PopulateSkBitmapWithResource(&sk_bitmap_, resource);
}

ResourceProvider::ScopedReadLockSoftware::~ScopedReadLockSoftware()
{
    resource_provider_->UnlockForRead(resource_id_);
}

ResourceProvider::ScopedReadLockSkImage::ScopedReadLockSkImage(
    ResourceProvider* resource_provider,
    ResourceId resource_id)
    : resource_provider_(resource_provider)
    , resource_id_(resource_id)
{
    const Resource* resource = resource_provider->LockForRead(resource_id);
    if (resource->gl_id) {
        //     GrGLTextureInfo texture_info;
        //     texture_info.fID = resource->gl_id;
        //     texture_info.fTarget = resource->target;
        //     GrBackendTextureDesc desc;
        //     desc.fWidth = resource->size.width();
        //     desc.fHeight = resource->size.height();
        //     desc.fConfig = ToGrPixelConfig(resource->format);
        //     desc.fOrigin = kTopLeft_GrSurfaceOrigin;
        //     desc.fTextureHandle = skia::GrGLTextureInfoToGrBackendObject(texture_info);
        //     sk_image_ = SkImage::MakeFromTexture(
        //         resource_provider->compositor_context_provider_->GrContext(), desc,
        //         kPremul_SkAlphaType,
        //         resource_provider->GetResourceSkColorSpace(resource), nullptr, nullptr);
    } else if (resource->pixels) {
        SkBitmap sk_bitmap;
        resource_provider->PopulateSkBitmapWithResource(&sk_bitmap, resource);
        sk_bitmap.setImmutable();
        sk_image_ = SkImage::MakeFromBitmap(sk_bitmap);
    } else {
        // During render process shutdown, ~RenderMessageFilter which calls
        // ~HostSharedBitmapClient (which deletes shared bitmaps from child)
        // can race with OnBeginFrameDeadline which draws a frame.
        // In these cases, shared bitmaps (and this read lock) won't be valid.
        // Renderers need to silently handle locks failing until this race
        // is fixed.  DCHECK that this is the only case where there are no pixels.
        DCHECK(!resource->shared_bitmap_id.IsZero());
    }
}

ResourceProvider::ScopedReadLockSkImage::~ScopedReadLockSkImage()
{
    resource_provider_->UnlockForRead(resource_id_);
}

ResourceProvider::ScopedWriteLockSoftware::ScopedWriteLockSoftware(
    ResourceProvider* resource_provider,
    ResourceId resource_id)
    : resource_provider_(resource_provider)
    , resource_id_(resource_id)
{
    Resource* resource = resource_provider->LockForWrite(resource_id);
    resource_provider->PopulateSkBitmapWithResource(&sk_bitmap_, resource);
    sk_color_space_ = resource_provider->GetResourceSkColorSpace(resource);
    DCHECK(valid());
}

ResourceProvider::ScopedWriteLockSoftware::~ScopedWriteLockSoftware()
{
    DCHECK(thread_checker_.CalledOnValidThread());
    Resource* resource = resource_provider_->GetResource(resource_id_);
    DCHECK(resource);
    resource->SetSynchronized();
    resource_provider_->UnlockForWrite(resource);
}

ResourceProvider::ScopedWriteLockGpuMemoryBuffer::
    ScopedWriteLockGpuMemoryBuffer(ResourceProvider* resource_provider,
        ResourceId resource_id)
    : resource_provider_(resource_provider)
    , resource_id_(resource_id)
{
    Resource* resource = resource_provider->LockForWrite(resource_id);
    DCHECK(IsGpuResourceType(resource->type));
    format_ = resource->format;
    size_ = resource->size;
    usage_ = resource->usage;
    gpu_memory_buffer_ = std::move(resource->gpu_memory_buffer);
    resource->gpu_memory_buffer = nullptr;
}

ResourceProvider::ScopedWriteLockGpuMemoryBuffer::
    ~ScopedWriteLockGpuMemoryBuffer()
{
    DCHECK(thread_checker_.CalledOnValidThread());
    Resource* resource = resource_provider_->GetResource(resource_id_);
    DCHECK(resource);
    if (gpu_memory_buffer_) {
        //     if (resource_provider_->enable_color_correct_rendering_)
        //       gpu_memory_buffer_->SetColorSpaceForScanout(resource->color_space);
        //     DCHECK(!resource->gpu_memory_buffer);
        //     resource_provider_->LazyCreate(resource);
        //     resource->gpu_memory_buffer = std::move(gpu_memory_buffer_);
        //     resource->allocated = true;
        //     resource_provider_->LazyCreateImage(resource);
        //     resource->dirty_image = true;
        //     resource->is_overlay_candidate = true;
        //     // GpuMemoryBuffer provides direct access to the memory used by the GPU.
        //     // Read lock fences are required to ensure that we're not trying to map a
        //     // buffer that is currently in-use by the GPU.
        //     resource->read_lock_fences_enabled = true;
        DebugBreak();
    }
    resource->SetSynchronized();
    resource_provider_->UnlockForWrite(resource);
}

gfx::GpuMemoryBuffer*
ResourceProvider::ScopedWriteLockGpuMemoryBuffer::GetGpuMemoryBuffer()
{
    if (!gpu_memory_buffer_) {
        //     gpu_memory_buffer_ =
        //         resource_provider_->gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
        //             size_, BufferFormat(format_), usage_, gpu::kNullSurfaceHandle);
        DebugBreak();
    }
    return gpu_memory_buffer_.get();
}

ResourceProvider::SynchronousFence::SynchronousFence(
    gpu::gles2::GLES2Interface* gl)
    : gl_(gl)
    , has_synchronized_(true)
{
}

ResourceProvider::SynchronousFence::~SynchronousFence() { }

void ResourceProvider::SynchronousFence::Set()
{
    has_synchronized_ = false;
}

bool ResourceProvider::SynchronousFence::HasPassed()
{
    if (!has_synchronized_) {
        has_synchronized_ = true;
        Synchronize();
    }
    return true;
}

void ResourceProvider::SynchronousFence::Wait()
{
    HasPassed();
}

void ResourceProvider::SynchronousFence::Synchronize()
{
    TRACE_EVENT0("cc", "ResourceProvider::SynchronousFence::Synchronize");
    gl_->Finish();
}

int ResourceProvider::CreateChild(const ReturnCallback& return_callback)
{
    DCHECK(thread_checker_.CalledOnValidThread());

    Child child_info;
    child_info.return_callback = return_callback;

    int child = next_child_++;
    children_[child] = child_info;
    return child;
}

void ResourceProvider::SetChildNeedsSyncTokens(int child_id, bool needs)
{
    ChildMap::iterator it = children_.find(child_id);
    DCHECK(it != children_.end());
    it->second.needs_sync_tokens = needs;
}

void ResourceProvider::DestroyChild(int child_id)
{
    ChildMap::iterator it = children_.find(child_id);
    DCHECK(it != children_.end());
    DestroyChildInternal(it, NORMAL);
}

void ResourceProvider::DestroyChildInternal(ChildMap::iterator it,
    DeleteStyle style)
{
    DCHECK(thread_checker_.CalledOnValidThread());

    Child& child = it->second;
    DCHECK(style == FOR_SHUTDOWN || !child.marked_for_deletion);

    ResourceIdArray resources_for_child;

    for (ResourceIdMap::iterator child_it = child.child_to_parent_map.begin();
         child_it != child.child_to_parent_map.end(); ++child_it) {
        ResourceId id = child_it->second;
        resources_for_child.push_back(id);
    }

    child.marked_for_deletion = true;

    DeleteAndReturnUnusedResourcesToChild(it, style, resources_for_child);
}

const ResourceProvider::ResourceIdMap& ResourceProvider::GetChildToParentMap(
    int child) const
{
    DCHECK(thread_checker_.CalledOnValidThread());
    ChildMap::const_iterator it = children_.find(child);
    DCHECK(it != children_.end());
    DCHECK(!it->second.marked_for_deletion);
    return it->second.child_to_parent_map;
}

void ResourceProvider::PrepareSendToParent(const ResourceIdArray& resource_ids,
    TransferableResourceArray* list)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    GLES2Interface* gl = ContextGL();

    // This function goes through the array multiple times, store the resources
    // as pointers so we don't have to look up the resource id multiple times.
    std::vector<Resource*> resources;
    resources.reserve(resource_ids.size());
    for (const ResourceId id : resource_ids) {
        Resource* resource = GetResource(id);
        // Check the synchronization and sync token state when delegated sync points
        // are required. The only case where we allow a sync token to not be set is
        // the case where the image is dirty. In that case we will bind the image
        // lazily and generate a sync token at that point.
        DCHECK(!delegated_sync_points_required_ || resource->dirty_image || !resource->needs_sync_token());

        // If we are validating the resource to be sent, the resource cannot be
        // in a LOCALLY_USED state. It must have been properly synchronized.
        DCHECK(!delegated_sync_points_required_ || Resource::LOCALLY_USED != resource->synchronization_state());

        resources.push_back(resource);
    }

    // Lazily create any mailboxes and verify all unverified sync tokens.
    std::vector<GLbyte*> unverified_sync_tokens;
    std::vector<Resource*> need_synchronization_resources;
    for (Resource* resource : resources) {
        if (!IsGpuResourceType(resource->type))
            continue;

        CreateMailboxAndBindResource(gl, resource);

        if (delegated_sync_points_required_) {
            if (resource->needs_sync_token()) {
                need_synchronization_resources.push_back(resource);
            } else if (resource->mailbox().HasSyncToken() && !resource->mailbox().sync_token().verified_flush()) {
                unverified_sync_tokens.push_back(resource->GetSyncTokenData());
            }
        }
    }

    // Insert sync point to synchronize the mailbox creation or bound textures.
    gpu::SyncToken new_sync_token;
    if (!need_synchronization_resources.empty()) {
        DCHECK(delegated_sync_points_required_);
        DCHECK(gl);
        const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
        gl->OrderingBarrierCHROMIUM();
        gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, new_sync_token.GetData());
        unverified_sync_tokens.push_back(new_sync_token.GetData());
    }

    if (!unverified_sync_tokens.empty()) {
        DCHECK(delegated_sync_points_required_);
        DCHECK(gl);
        gl->VerifySyncTokensCHROMIUM(unverified_sync_tokens.data(),
            unverified_sync_tokens.size());
    }

    // Set sync token after verification.
    for (Resource* resource : need_synchronization_resources) {
        DCHECK(IsGpuResourceType(resource->type));
        resource->UpdateSyncToken(new_sync_token);
        resource->SetSynchronized();
    }

    // Transfer Resources
    DCHECK_EQ(resources.size(), resource_ids.size());
    for (size_t i = 0; i < resources.size(); ++i) {
        Resource* source = resources[i];
        const ResourceId id = resource_ids[i];

        DCHECK(!delegated_sync_points_required_ || !source->needs_sync_token());
        DCHECK(!delegated_sync_points_required_ || Resource::LOCALLY_USED != source->synchronization_state());

        TransferableResource resource;
        TransferResource(source, id, &resource);

        source->exported_count++;
        list->push_back(resource);
    }
}

void ResourceProvider::ReceiveFromChild(
    int child,
    const TransferableResourceArray& resources)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    GLES2Interface* gl = ContextGL();
    Child& child_info = children_.find(child)->second;
    DCHECK(!child_info.marked_for_deletion);
    for (TransferableResourceArray::const_iterator it = resources.begin();
         it != resources.end(); ++it) {
        ResourceIdMap::iterator resource_in_map_it = child_info.child_to_parent_map.find(it->id);
        if (resource_in_map_it != child_info.child_to_parent_map.end()) {
            Resource* resource = GetResource(resource_in_map_it->second);
            resource->marked_for_deletion = false;
            resource->imported_count++;
            continue;
        }

        if ((!it->is_software && !gl) || (it->is_software && !shared_bitmap_manager_)) {
            TRACE_EVENT0("cc", "ResourceProvider::ReceiveFromChild dropping invalid");
            ReturnedResourceArray to_return;
            to_return.push_back(it->ToReturnedResource());
            child_info.return_callback.Run(to_return,
                blocking_main_thread_task_runner_);
            continue;
        }

        ResourceId local_id = next_id_++;
        Resource* resource = nullptr;
        if (it->is_software) {
            resource = InsertResource(local_id,
                Resource(it->mailbox_holder.mailbox, it->size,
                    Resource::DELEGATED, GL_LINEAR));
        } else {
            resource = InsertResource(
                local_id, Resource(0, it->size, Resource::DELEGATED, it->mailbox_holder.texture_target, it->filter, TEXTURE_HINT_IMMUTABLE, RESOURCE_TYPE_GL_TEXTURE, it->format));
            resource->set_mailbox(TextureMailbox(it->mailbox_holder.mailbox,
                it->mailbox_holder.sync_token,
                it->mailbox_holder.texture_target));
            resource->read_lock_fences_enabled = it->read_lock_fences_enabled;
            resource->is_overlay_candidate = it->is_overlay_candidate;
#if defined(OS_ANDROID)
            resource->is_backed_by_surface_texture = it->is_backed_by_surface_texture;
            resource->wants_promotion_hint = it->wants_promotion_hint;
            if (resource->wants_promotion_hint)
                wants_promotion_hints_set_.insert(local_id);
#endif
            resource->color_space = it->color_space;
        }
        resource->child_id = child;
        // Don't allocate a texture for a child.
        resource->allocated = true;
        resource->imported_count = 1;
        child_info.parent_to_child_map[local_id] = it->id;
        child_info.child_to_parent_map[it->id] = local_id;
    }
}

void ResourceProvider::DeclareUsedResourcesFromChild(
    int child,
    const ResourceIdSet& resources_from_child)
{
    DCHECK(thread_checker_.CalledOnValidThread());

    ChildMap::iterator child_it = children_.find(child);
    DCHECK(child_it != children_.end());
    Child& child_info = child_it->second;
    DCHECK(!child_info.marked_for_deletion);

    ResourceIdArray unused;
    for (ResourceIdMap::iterator it = child_info.child_to_parent_map.begin();
         it != child_info.child_to_parent_map.end(); ++it) {
        ResourceId local_id = it->second;
        bool resource_is_in_use = resources_from_child.count(it->first) > 0;
        if (!resource_is_in_use)
            unused.push_back(local_id);
    }
    DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
}

void ResourceProvider::ReceiveReturnsFromParent(
    const ReturnedResourceArray& resources)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    GLES2Interface* gl = ContextGL();

    std::unordered_map<int, ResourceIdArray> resources_for_child;

    for (const ReturnedResource& returned : resources) {
        ResourceId local_id = returned.id;
        ResourceMap::iterator map_iterator = resources_.find(local_id);
        // Resource was already lost (e.g. it belonged to a child that was
        // destroyed).
        if (map_iterator == resources_.end())
            continue;

        Resource* resource = &map_iterator->second;

        CHECK_GE(resource->exported_count, returned.count);
        resource->exported_count -= returned.count;
        resource->lost |= returned.lost;
        if (resource->exported_count)
            continue;

        if (returned.sync_token.HasData()) {
            DCHECK(!resource->has_shared_bitmap_id);
            if (resource->origin == Resource::INTERNAL) {
                DCHECK(resource->gl_id);
                DCHECK(returned.sync_token.HasData());
                gl->WaitSyncTokenCHROMIUM(returned.sync_token.GetConstData());
                resource->SetSynchronized();
            } else {
                DCHECK(!resource->gl_id);
                resource->UpdateSyncToken(returned.sync_token);
            }
        }

        if (!resource->marked_for_deletion)
            continue;

        if (!resource->child_id) {
            // The resource belongs to this ResourceProvider, so it can be destroyed.
            DeleteResourceInternal(map_iterator, NORMAL);
            continue;
        }

        DCHECK(resource->origin == Resource::DELEGATED);
        resources_for_child[resource->child_id].push_back(local_id);
    }

    for (const auto& children : resources_for_child) {
        ChildMap::iterator child_it = children_.find(children.first);
        DCHECK(child_it != children_.end());
        DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, children.second);
    }
}

#if defined(OS_ANDROID)
void ResourceProvider::SendPromotionHints(
    const OverlayCandidateList::PromotionHintInfoMap& promotion_hints)
{
    GLES2Interface* gl = ContextGL();
    if (!gl)
        return;

    for (const auto& id : wants_promotion_hints_set_) {
        const ResourceMap::iterator it = resources_.find(id);
        if (it == resources_.end())
            continue;

        if (it->second.marked_for_deletion)
            continue;

        const Resource* resource = LockForRead(id);
        DCHECK(resource->wants_promotion_hint);

        // Insist that this is backed by a GPU texture.
        if (IsGpuResourceType(resource->type)) {
            DCHECK(resource->gl_id);
            auto iter = promotion_hints.find(id);
            bool promotable = iter != promotion_hints.end();
            gl->OverlayPromotionHintCHROMIUM(resource->gl_id, promotable,
                promotable ? iter->second.x() : 0,
                promotable ? iter->second.y() : 0);
        }
        UnlockForRead(id);
    }
}
#endif

void ResourceProvider::CreateMailboxAndBindResource(
    gpu::gles2::GLES2Interface* gl,
    Resource* resource)
{
    DCHECK(IsGpuResourceType(resource->type));
    DCHECK(gl);

    if (!resource->mailbox().IsValid()) {
        LazyCreate(resource);

        gpu::MailboxHolder mailbox_holder;
        mailbox_holder.texture_target = resource->target;
        gl->GenMailboxCHROMIUM(mailbox_holder.mailbox.name);
        gl->ProduceTextureDirectCHROMIUM(resource->gl_id,
            mailbox_holder.texture_target,
            mailbox_holder.mailbox.name);
        resource->set_mailbox(TextureMailbox(mailbox_holder));
    }

    if (resource->image_id && resource->dirty_image) {
        DCHECK(resource->gl_id);
        DCHECK(resource->origin == Resource::INTERNAL);
        BindImageForSampling(resource);
    }
}

void ResourceProvider::TransferResource(Resource* source,
    ResourceId id,
    TransferableResource* resource)
{
    DCHECK(!source->locked_for_write);
    DCHECK(!source->lock_for_read_count);
    DCHECK(source->origin != Resource::EXTERNAL || source->mailbox().IsValid());
    DCHECK(source->allocated);
    resource->id = id;
    resource->format = source->format;
    resource->mailbox_holder.texture_target = source->target;
    resource->filter = source->filter;
    resource->size = source->size;
    resource->read_lock_fences_enabled = source->read_lock_fences_enabled;
    resource->is_overlay_candidate = source->is_overlay_candidate;
#if defined(OS_ANDROID)
    resource->is_backed_by_surface_texture = source->is_backed_by_surface_texture;
    resource->wants_promotion_hint = source->wants_promotion_hint;
#endif
    resource->color_space = source->color_space;

    if (source->type == RESOURCE_TYPE_BITMAP) {
        resource->mailbox_holder.mailbox = source->shared_bitmap_id;
        resource->is_software = true;
    } else {
        DCHECK(source->mailbox().IsValid());
        DCHECK(source->mailbox().IsTexture());
        DCHECK(!source->image_id || !source->dirty_image);
        // This is either an external resource, or a compositor resource that we
        // already exported. Make sure to forward the sync point that we were given.
        resource->mailbox_holder.mailbox = source->mailbox().mailbox();
        resource->mailbox_holder.texture_target = source->mailbox().target();
        resource->mailbox_holder.sync_token = source->mailbox().sync_token();
    }
}

void ResourceProvider::DeleteAndReturnUnusedResourcesToChild(
    ChildMap::iterator child_it,
    DeleteStyle style,
    const ResourceIdArray& unused)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    DCHECK(child_it != children_.end());
    Child* child_info = &child_it->second;

    if (unused.empty() && !child_info->marked_for_deletion)
        return;

    ReturnedResourceArray to_return;
    to_return.reserve(unused.size());
    std::vector<ReturnedResource*> need_synchronization_resources;
    std::vector<GLbyte*> unverified_sync_tokens;

    GLES2Interface* gl = ContextGL();

    for (ResourceId local_id : unused) {
        ResourceMap::iterator it = resources_.find(local_id);
        CHECK(it != resources_.end());
        Resource& resource = it->second;

        DCHECK(!resource.locked_for_write);
        DCHECK(child_info->parent_to_child_map.count(local_id));

        ResourceId child_id = child_info->parent_to_child_map[local_id];
        DCHECK(child_info->child_to_parent_map.count(child_id));

        bool is_lost = resource.lost || (IsGpuResourceType(resource.type) && lost_context_provider_);
        if (resource.exported_count > 0 || resource.lock_for_read_count > 0) {
            if (style != FOR_SHUTDOWN) {
                // Defer this resource deletion.
                resource.marked_for_deletion = true;
                continue;
            }
            // We can't postpone the deletion, so we'll have to lose it.
            is_lost = true;
        } else if (!ReadLockFenceHasPassed(&resource)) {
            // TODO(dcastagna): see if it's possible to use this logic for
            // the branch above too, where the resource is locked or still exported.
            if (style != FOR_SHUTDOWN && !child_info->marked_for_deletion) {
                // Defer this resource deletion.
                resource.marked_for_deletion = true;
                continue;
            }
            // We can't postpone the deletion, so we'll have to lose it.
            is_lost = true;
        }

        if (IsGpuResourceType(resource.type) && resource.filter != resource.original_filter) {
            DCHECK(resource.target);
            DCHECK(resource.gl_id);
            DCHECK(gl);
            gl->BindTexture(resource.target, resource.gl_id);
            gl->TexParameteri(resource.target, GL_TEXTURE_MIN_FILTER,
                resource.original_filter);
            gl->TexParameteri(resource.target, GL_TEXTURE_MAG_FILTER,
                resource.original_filter);
            resource.SetLocallyUsed();
        }

        ReturnedResource returned;
        returned.id = child_id;
        returned.sync_token = resource.mailbox().sync_token();
        returned.count = resource.imported_count;
        returned.lost = is_lost;
        to_return.push_back(returned);

        if (IsGpuResourceType(resource.type) && child_info->needs_sync_tokens) {
            if (resource.needs_sync_token()) {
                need_synchronization_resources.push_back(&to_return.back());
            } else if (returned.sync_token.HasData() && !returned.sync_token.verified_flush()) {
                // Before returning any sync tokens, they must be verified.
                unverified_sync_tokens.push_back(returned.sync_token.GetData());
            }
        }

        child_info->parent_to_child_map.erase(local_id);
        child_info->child_to_parent_map.erase(child_id);
        resource.imported_count = 0;
        DeleteResourceInternal(it, style);
    }

    gpu::SyncToken new_sync_token;
    if (!need_synchronization_resources.empty()) {
        DCHECK(child_info->needs_sync_tokens);
        DCHECK(gl);
        const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
        gl->OrderingBarrierCHROMIUM();
        gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, new_sync_token.GetData());
        unverified_sync_tokens.push_back(new_sync_token.GetData());
    }

    if (!unverified_sync_tokens.empty()) {
        DCHECK(child_info->needs_sync_tokens);
        DCHECK(gl);
        gl->VerifySyncTokensCHROMIUM(unverified_sync_tokens.data(),
            unverified_sync_tokens.size());
    }

    // Set sync token after verification.
    for (ReturnedResource* returned : need_synchronization_resources)
        returned->sync_token = new_sync_token;

    if (!to_return.empty())
        child_info->return_callback.Run(to_return,
            blocking_main_thread_task_runner_);

    if (child_info->marked_for_deletion && child_info->parent_to_child_map.empty()) {
        DCHECK(child_info->child_to_parent_map.empty());
        children_.erase(child_it);
    }
}

GLenum ResourceProvider::BindForSampling(ResourceId resource_id,
    GLenum unit,
    GLenum filter)
{
    DCHECK(thread_checker_.CalledOnValidThread());
    GLES2Interface* gl = ContextGL();
    ResourceMap::iterator it = resources_.find(resource_id);
    DCHECK(it != resources_.end());
    Resource* resource = &it->second;
    DCHECK(resource->lock_for_read_count);
    DCHECK(!resource->locked_for_write);

    ScopedSetActiveTexture scoped_active_tex(gl, unit);
    GLenum target = resource->target;
    gl->BindTexture(target, resource->gl_id);
    if (filter != resource->filter) {
        gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
        gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
        resource->filter = filter;
    }

    if (resource->image_id && resource->dirty_image)
        BindImageForSampling(resource);

    return target;
}

void ResourceProvider::CreateForTesting(ResourceId id)
{
    LazyCreate(GetResource(id));
}

void ResourceProvider::LazyCreate(Resource* resource)
{
    if (!IsGpuResourceType(resource->type) || resource->origin != Resource::INTERNAL)
        return;

    if (resource->gl_id)
        return;

    DCHECK(resource->origin == Resource::INTERNAL);
    DCHECK(!resource->mailbox().IsValid());
    resource->gl_id = texture_id_allocator_->NextId();

    GLES2Interface* gl = ContextGL();
    DCHECK(gl);

    // Create and set texture properties. Allocation is delayed until needed.
    gl->BindTexture(resource->target, resource->gl_id);
    gl->TexParameteri(resource->target, GL_TEXTURE_MIN_FILTER,
        resource->original_filter);
    gl->TexParameteri(resource->target, GL_TEXTURE_MAG_FILTER,
        resource->original_filter);
    gl->TexParameteri(resource->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    gl->TexParameteri(resource->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    if (use_texture_usage_hint_ && (resource->hint & TEXTURE_HINT_FRAMEBUFFER)) {
        gl->TexParameteri(resource->target, GL_TEXTURE_USAGE_ANGLE,
            GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
    }
}

void ResourceProvider::AllocateForTesting(ResourceId id)
{
    LazyAllocate(GetResource(id));
}

void ResourceProvider::LazyAllocate(Resource* resource)
{
    DCHECK(resource);
    if (resource->allocated)
        return;
    LazyCreate(resource);
    if (!resource->gl_id)
        return;
    resource->allocated = true;
    GLES2Interface* gl = ContextGL();
    gfx::Size& size = resource->size;
    ResourceFormat format = resource->format;
    gl->BindTexture(resource->target, resource->gl_id);
    if (resource->type == RESOURCE_TYPE_GPU_MEMORY_BUFFER) {
        //     resource->gpu_memory_buffer =
        //         gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
        //             size, BufferFormat(format), resource->usage,
        //             gpu::kNullSurfaceHandle);
        //     if (resource->gpu_memory_buffer && enable_color_correct_rendering_) {
        //       resource->gpu_memory_buffer->SetColorSpaceForScanout(
        //           resource->color_space);
        //     }
        DebugBreak();

        LazyCreateImage(resource);
        resource->dirty_image = true;
        resource->is_overlay_candidate = true;
        // GpuMemoryBuffer provides direct access to the memory used by the GPU.
        // Read lock fences are required to ensure that we're not trying to map a
        // buffer that is currently in-use by the GPU.
        resource->read_lock_fences_enabled = true;
    } else if (use_texture_storage_ext_ && IsFormatSupportedForStorage(format, use_texture_format_bgra_) && (resource->hint & TEXTURE_HINT_IMMUTABLE)) {
        GLenum storage_format = TextureToStorageFormat(format);
        gl->TexStorage2DEXT(resource->target, 1, storage_format, size.width(),
            size.height());
    } else {
        // ETC1 does not support preallocation.
        if (format != ETC1) {
            gl->TexImage2D(resource->target, 0, GLInternalFormat(format),
                size.width(), size.height(), 0, GLDataFormat(format),
                GLDataType(format), nullptr);
        }
    }
}

void ResourceProvider::LazyCreateImage(Resource* resource)
{
    DCHECK(resource->gpu_memory_buffer);
    DCHECK(resource->gl_id);
    DCHECK(resource->allocated);
    // Avoid crashing in release builds if GpuMemoryBuffer allocation fails.
    // http://crbug.com/554541
    if (!resource->gpu_memory_buffer)
        return;
    if (!resource->image_id) {
        GLES2Interface* gl = ContextGL();
        DCHECK(gl);

#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
        // TODO(reveman): This avoids a performance problem on ARM ChromeOS
        // devices. This only works with shared memory backed buffers.
        // crbug.com/580166
        DCHECK_EQ(resource->gpu_memory_buffer->GetHandle().type,
            gfx::SHARED_MEMORY_BUFFER);
#endif
        resource->image_id = gl->CreateImageCHROMIUM(
            resource->gpu_memory_buffer->AsClientBuffer(), resource->size.width(),
            resource->size.height(), GLInternalFormat(resource->format));
        DCHECK(resource->image_id || IsGLContextLost());

        resource->SetLocallyUsed();
    }
}

void ResourceProvider::BindImageForSampling(Resource* resource)
{
    GLES2Interface* gl = ContextGL();
    DCHECK(resource->gl_id);
    DCHECK(resource->image_id);

    // Release image currently bound to texture.
    gl->BindTexture(resource->target, resource->gl_id);
    if (resource->bound_image_id)
        gl->ReleaseTexImage2DCHROMIUM(resource->target, resource->bound_image_id);
    gl->BindTexImage2DCHROMIUM(resource->target, resource->image_id);
    resource->bound_image_id = resource->image_id;
    resource->dirty_image = false;
    resource->SetLocallyUsed();
}

void ResourceProvider::WaitSyncTokenIfNeeded(ResourceId id)
{
    Resource* resource = GetResource(id);
    DCHECK_EQ(resource->exported_count, 0);
    DCHECK(resource->allocated);
    if (Resource::NEEDS_WAIT == resource->synchronization_state()) {
        DCHECK(IsGpuResourceType(resource->type));

        GLES2Interface* gl = ContextGL();
        DCHECK(gl);
        resource->WaitSyncToken(gl);
    }
}

GLint ResourceProvider::GetActiveTextureUnit(GLES2Interface* gl)
{
    GLint active_unit = 0;
    gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit);
    return active_unit;
}

GLenum ResourceProvider::GetImageTextureTarget(gfx::BufferUsage usage,
    ResourceFormat format)
{
    gfx::BufferFormat buffer_format = BufferFormat(format);
    auto found = buffer_to_texture_target_map_.find(
        BufferToTextureTargetKey(usage, buffer_format));
    DCHECK(found != buffer_to_texture_target_map_.end());
    return found->second;
}

void ResourceProvider::ValidateResource(ResourceId id) const
{
    DCHECK(thread_checker_.CalledOnValidThread());
    DCHECK(id);
    DCHECK(resources_.find(id) != resources_.end());
}

GLES2Interface* ResourceProvider::ContextGL() const
{
    ContextProvider* context_provider = compositor_context_provider_;
    return context_provider ? context_provider->ContextGL() : nullptr;
}

bool ResourceProvider::IsGLContextLost() const
{
    return ContextGL()->GetGraphicsResetStatusKHR() != GL_NO_ERROR;
}

bool ResourceProvider::OnMemoryDump(
    const base::trace_event::MemoryDumpArgs& args,
    base::trace_event::ProcessMemoryDump* pmd)
{
    DCHECK(thread_checker_.CalledOnValidThread());

    const uint64_t tracing_process_id = base::trace_event::MemoryDumpManager::GetInstance()
                                            ->GetTracingProcessId();

    for (const auto& resource_entry : resources_) {
        const auto& resource = resource_entry.second;

        bool backing_memory_allocated = false;
        switch (resource.type) {
        case RESOURCE_TYPE_GPU_MEMORY_BUFFER:
            backing_memory_allocated = !!resource.gpu_memory_buffer;
            break;
        case RESOURCE_TYPE_GL_TEXTURE:
            backing_memory_allocated = !!resource.gl_id;
            break;
        case RESOURCE_TYPE_BITMAP:
            backing_memory_allocated = resource.has_shared_bitmap_id;
            break;
        }

        if (!backing_memory_allocated) {
            // Don't log unallocated resources - they have no backing memory.
            continue;
        }

        // Resource IDs are not process-unique, so log with the ResourceProvider's
        // unique id.
        std::string dump_name = base::StringPrintf("cc/resource_memory/provider_%d/resource_%d",
            tracing_id_, resource_entry.first);
        base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name);

        uint64_t total_bytes = ResourceUtil::UncheckedSizeInBytesAligned<size_t>(
            resource.size, resource.format);
        dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
            base::trace_event::MemoryAllocatorDump::kUnitsBytes,
            static_cast<uint64_t>(total_bytes));

        // Resources may be shared across processes and require a shared GUID to
        // prevent double counting the memory.
        base::trace_event::MemoryAllocatorDumpGuid guid;
        switch (resource.type) {
        case RESOURCE_TYPE_GPU_MEMORY_BUFFER:
            //         guid = gfx::GetGpuMemoryBufferGUIDForTracing(
            //             tracing_process_id, resource.gpu_memory_buffer->GetHandle().id);
            DebugBreak();
            break;
        case RESOURCE_TYPE_GL_TEXTURE:
            //         DCHECK(resource.gl_id);
            //         guid = gl::GetGLTextureClientGUIDForTracing(
            //             compositor_context_provider_->ContextSupport()
            //                 ->ShareGroupTracingGUID(),
            //             resource.gl_id);
            DebugBreak();
            break;
        case RESOURCE_TYPE_BITMAP:
            DCHECK(resource.has_shared_bitmap_id);
            guid = GetSharedBitmapGUIDForTracing(resource.shared_bitmap_id);
            break;
        }

        DCHECK(!guid.empty());

        const int kImportance = 2;
        pmd->CreateSharedGlobalAllocatorDump(guid);
        pmd->AddOwnershipEdge(dump->guid(), guid, kImportance);
    }

    return true;
}

} // namespace cc
